﻿#pragma once

#include "../DeviceLayer/Device.h"
#include "Service.h"
#include "DataPool.h"
#include "../PublicService/SettingData.h"
#include <thread>
#include <QMap>

using namespace DeviceLayer;

namespace BusinessLayer {

/**
 * @brief 所有对象容器的父类。
 */
class Container
{
protected:
    //保存各个对象的shared_ptr
    std::vector<std::shared_ptr<Object>> objects;
    //用于映射对象名称到objects索引的Map
    QMap<QString, int> objectNameMap;

public:
    Container() {}
    Container(const Container&) = delete; //Disable copy constructor


    /**
     * @brief 获取容器中对象数量
     * @return 对象数量
     */
    inline size_t size() const
    {
        return objects.size();
    }

    /**
     * @brief 从SettingData.xml文件指定的路径获取类名和对象名，生成对象并保存到objects容器中。
     * @param path SettingData.xml中的一个带有下标模板的路径，比如：Device/Devices[%1]，表示Device下有多个Devices结点。
     * @return 成功返回true，失败返回false。
     */
    template<typename T>
    bool CreateObjectsFromSettingData(const QString& path)
    {
        int index = 0;
        while (true)
        {
            QString className = SettingData::instance.GetDataString(path.arg(index) + "::class");
            QString objectName = SettingData::instance.GetDataString(path.arg(index) + "::name");
            if (className.isEmpty())    //如果取不到类名称，则表示没有其他对象需要创建，退出。
                break;
            if (objectName.isEmpty())
            {
                LOGERR("Container::CreateObjectsFromSettingData", "对象没有名称。class = " + className);
                return false;
            }
            try
            {
                //通过对象工厂创建对象，放入Services vector中。
                std::shared_ptr<T> objectPtr = std::dynamic_pointer_cast<T>(ObjectFactory::instance().CreateObj(className));
                objects.push_back(objectPtr);
                if (!objectName.isNull() && !objectName.isEmpty())
                {
                    //如果对象有名称，要插入到objectNameMap中，以便能够通过名称获取对象的shared_ptr
                    objectPtr->SetName(objectName);
                    objectNameMap.insert(objectName, static_cast<int>(objects.size()) - 1);
                }

                //对象自己的初始化工作
                objectPtr->InitFromSettingData(path.arg(index));
            }
            catch (...)
            {
                LOGERR("Container::CreateObjectsFromSettingData", "创建对象失败。class = " + className + ", name = " + objectName);
                return false;
            }

            index++;
        }

        return true;
    }
};

/**
 * @brief 设备对象容器。
 *        通过解析SettingData.xml中的<Devices>结点对各个<Device>类对象的定义，自动生成设备对象并放入容器中
 */
class ContainerForDevice : public Container
{
private:
    //用于自动调用设备对象的QueryData方法的线程
    void QueryData();        //The loop thread.
    std::thread queryDataThread;
    std::atomic<bool> run { false };

    //设备的数据池
    std::shared_ptr<DataPool> dataPool;
public:
    ContainerForDevice();
    ContainerForDevice(const ContainerForDevice&) = delete; //Disable copy constructor
    ~ContainerForDevice();

    /**
     * @brief 通过对象名称为索引，从容器中获取一个对象
     * @param name 对象名称，必须是定义在SettingData.xml的Devices中的各个Device的name属性
     * @return 如果成功，返回该对象的shared_ptr；否则抛出异常
     */
    inline std::shared_ptr<Device> operator[](const QString& name)
    {
        return operator[](objectNameMap.value(name, -1));
    }

    /**
     * @brief 通过对象名称为索引，从容器中获取一个对象
     * @param index 对象索引值，从0开始
     * @return 如果成功，返回该对象的shared_ptr；否则抛出异常
     */
    inline std::shared_ptr<Device> operator[](const int index)
    {
        return std::dynamic_pointer_cast<Device>(objects[index]);
    }

    /**
     * @brief 通过读取SettingData.xml内的配置，创建设备对象。本函数需要显式调用。
     * @return 如果成功，返回true；如果失败，返回false。ErrLog.txt中将记录失败原因。通常的原因是SettingData.xml中Device的class属性不正确。
     */
    bool CreateFromSettingData();

    /**
     * @brief 启动线程，用于自动调用各个Device的QueryData方法，读取设备数据。
     *        本方法应该在CreateDevicesFromSettingData之后调用。
     *        线程会在ContainerForDevice类析构时自动停止。
     */
    void StartQueryDataThread();

    /**
     * @brief 获取数据池对象。数据池对象由ContainerForDevice类管理。
     * @return
     */
    inline std::shared_ptr<DataPool> getDataPool() { return dataPool; }

    /**
     * @brief 从数据池中获取数据
     * @param objFieldName 以 “对象名.字段名” 为形式的数据名。
     * @return 数据值
     */
    inline QVariant getPoolData(const QString& objFieldName) { return dataPool->get(objFieldName); }

    /**
     *  @brief ContainerForDevice的实例。请通过这个实例来使用ContainerForDevice
     */
    static ContainerForDevice instance;
};

/**
 * 用于简化在业务层代码中实现设备对象依赖注入的宏定义。className = 设备类名，objName = 在SettingData.xml中定义的对象名。
 * objName将被声明为std::shared_ptr<className>对象。
 * 将AUTO_WIRED_DEVICE用在类的声明中，在类的其他部分就可以使用这里的objName来访问对象。
 */
#define AUTO_WIRED_DEVICE(className, objName) std::shared_ptr<className> objName = std::dynamic_pointer_cast<className>(BusinessLayer::ContainerForDevice::instance[#objName]);

/**
 * 用于简化呈现层/业务层代码从数据池中获取数据的宏定义。objFieldName = 以 “设备对象名.字段名” 为形式的数据名称。
 */
#define GET_POOL_DATA(objFieldName) BusinessLayer::ContainerForDevice::instance.getPoolData(objFieldName)


/**
 * @brief 业务层服务对象容器
 *        通过解析SettingData.xml中的<Services>结点对各个<Service>类对象的定义，自动生成服务对象并放入容器中
 */
class ContainerForService : public Container
{
public:
    ContainerForService();
    ContainerForService(ContainerForService&) = delete;

    /**
     * @brief 通过对象名称为索引，从容器中获取一个对象
     * @param name 对象名称，必须是定义在SettingData.xml的Devices中的各个Service的name属性
     * @return 如果成功，返回该对象的shared_ptr；否则抛出异常
     */
    inline std::shared_ptr<Service> operator[](const QString& name)
    {
        return operator[](objectNameMap.value(name, -1));
    }

    /**
     * @brief 通过对象名称为索引，从容器中获取一个对象
     * @param index 对象索引值，从0开始
     * @return 如果成功，返回该对象的shared_ptr；否则抛出异常
     */
    inline std::shared_ptr<Service> operator[](const int index)
    {
        return std::dynamic_pointer_cast<Service>(objects[index]);
    }



    /**
     * @brief 通过读取SettingData.xml内的配置，创建服务对象。本函数需要显式调用。
     * @return 如果成功，返回true；如果失败，返回false。ErrLog.txt中将记录失败原因。通常的原因是SettingData.xml中Service的class属性不正确。
     */
    bool CreateFromSettingData();

    /**
     *  @brief ContainerForService的实例。请通过这个实例来使用ContainerForService
     */
    static ContainerForService instance;
};


/**
 * 用于简化在业务层代码中实现服务对象依赖注入的宏定义。className = 服务类名，objName = 在SettingData.xml中定义的对象名。
 * objName将被声明为std::shared_ptr<className>对象。
 * 将AUTO_WIRED_SERVICE用在类的声明中，在类的其他部分就可以使用这里的objName来访问对象。
 */
#define AUTO_WIRED_SERVICE(className, objName) std::shared_ptr<className> objName = std::dynamic_pointer_cast<className>(BusinessLayer::ContainerForService::instance[#objName]);

}
